﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using Ganosoft.DynamicProxy;
using Ganosoft.Aspect;

namespace GanoESB.Business
{
    public class ServiceController
    {
        private ComponentRuntime ctx;
        private ServiceDescriptor descr;
        private ServiceItem item;
        private ServiceBus serviceBus;
        private List<ServiceCall> callList;
        private ServiceDetail detail;
        private ServiceContractAttribute contract;
        private Type intfType;
        private object serviceProxy;

        public ServiceController(ServiceDescriptor descr, ServiceBus serviceBus)
        {
            this.descr = descr;
            this.serviceBus = serviceBus;
            this.callList = new List<ServiceCall>();

            this.ctx = serviceBus.GetComponentContext(descr.ComponentName);
            Assembly assembly = ctx.LoadIntfAssembly(descr);

            string[] strs = descr.ServiceInterface.Split(',');
            intfType = assembly.GetType(strs[0].Trim());
            serviceProxy = new ProxyFactory().CreateProxy(intfType, new ServiceInterceptor(this));

            object[] attrs = intfType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
            if (attrs.Length > 0)
                contract = (ServiceContractAttribute)attrs[0];

            serviceBus.RegisterMessage(descr.ComponentName, intfType, this);

            item = new ServiceItem();
            item.ServiceName = descr.ServiceName;
            item.ServiceInterface = descr.ServiceInterface;
            item.ServiceUrl = descr.ServiceUrl;

            detail = new ServiceDetail();
            detail.ServiceItem = item;

            if (intfType.IsSubclassOf(typeof(ISecurityChecker)))
               serviceBus.SecurityChecker = (ISecurityChecker)serviceProxy;
        }

        public void Start()
        {
            detail.StartTime = DateTime.Now;
            detail.State = 1; // 启动
        }

        public void Stop()
        {
            detail.State = 2; // 停止
        }

        public ServiceDetail ServiceDetail
        {
            get { return detail; }
        }

        public ServiceItem ServiceItem
        {
            get { return item; }
        }

        public ServiceCall[] ServiceCalls
        {
            get { return callList.ToArray(); }
        }

        public object ExecuteMethod(MethodInfo mi, IMessage message)
        {
            object[] args = message.GetArguments();
            return mi.Invoke(serviceProxy, args);
        }

        public object ExecuteMethod(ServiceInvocation invocation)
        {
            try
            {
                object target = CreateService();
                try
                {
                    return invocation.Method.Invoke(target, invocation.Args);
                }
                catch
                {
                    (target as ICommunicationObject).Abort();
                    throw;
                }
                finally
                {
                    (target as ICommunicationObject).Close();
                }
            }
            finally
            {
                detail.CallCount++;
                detail.LastExecuted = DateTime.Now;

                callList.Add(invocation.ServiceCall);
            }
        }

        private object CreateService()
        {
            if (contract == null || contract.CallbackContract == null)
            {
                var channelFactoryType = typeof(ChannelFactory<>);
                channelFactoryType = channelFactoryType.MakeGenericType(intfType);
                var channelFactory = Activator.CreateInstance(channelFactoryType);
                MethodInfo createchannel = channelFactory.GetType().GetMethod("CreateChannel", new Type[] { typeof(NetNamedPipeBinding), typeof(EndpointAddress) });

                return createchannel.Invoke(channelFactory, new object[] { new NetNamedPipeBinding(), new EndpointAddress(descr.ServiceUrl) });
            }
            else
            {
                var channelFactoryType = typeof(DuplexChannelFactory<>);
                channelFactoryType = channelFactoryType.MakeGenericType(intfType);
                ConstructorInfo ci = channelFactoryType.GetConstructor(new Type[] { typeof(InstanceContext) });

                InstanceContext ctx = new InstanceContext(serviceBus.Callback);

                var channelFactory = ci.Invoke(new object[] { ctx });
                MethodInfo createchannel = channelFactory.GetType().GetMethod("CreateChannel", new Type[] { typeof(IServiceFinder), typeof(NetNamedPipeBinding), typeof(EndpointAddress) });

                return createchannel.Invoke(channelFactory, new object[] { serviceBus.Callback, new NetNamedPipeBinding(), new EndpointAddress(descr.ServiceUrl) });
            }
        }

        public IServiceInterceptor[] GetInterceptors(string methodName)
        {
            return AspectContainer.Instance.GetInterceptors(descr.ServiceClazz, methodName);
        }
    }
}
